#pragma once 

/* 好友关系表的ODB映射操作 */

#include "mysql.hpp"
#include "friend_relation.hxx"
#include "friend_relation-odb.hxx"


namespace ChatSystem
{
    //好友关系表ODB操作
    class RelationTable
    {
    public:
        using ptr = std::shared_ptr<RelationTable>;

        RelationTable() = delete;
        RelationTable(const std::shared_ptr<odb::core::database>& db)
        :_db(db)
        {}
        ~RelationTable(){}

        //新增用户关系
        bool insert(const std::string& user_id, const std::string& peer_id)
        {
            //这里不直接传递Relation类型的参数是因为在表中新增关系信息的时候既要新增 {用户1，用户2}的好友关系 也要新增 {用户2，用户1} 的好友关系
            //如果直接传递Relation类型的参数，就需要使用者构造两个Relation类型的对象，并且调用两次Relation，显然传递两个用户ID是最好的
            //数据的所有操作都会有可能导致程序的崩溃，所以需要捕捉异常防止程序的退出；
            try
            {
                //构造Relation对象
                Relation r1(user_id, peer_id);
                Relation r2(peer_id, user_id);
                //获取对象开启事务
                odb::transaction trans(_db->begin());
                _db->persist(r1);
                _db->persist(r2);
                //提交事务
                trans.commit();
            }
            catch(const std::exception& e)
            {
                LOG_ERROR("新增好友关系 {}:{} 失败, {}", user_id, peer_id, e.what());
                return false;
            }
            return true;   
        }

        //移除用户关系
        bool remove(const std::string& user_id, const std::string& peer_id)
        {
            //数据的所有操作都会有可能导致程序的崩溃，所以需要捕捉异常防止程序的退出；
            try
            {
                //获取对象开启事务
                odb::transaction trans(_db->begin());
                //1. 先查询
                typedef odb::query<Relation> query;
                typedef odb::result<Relation> result;
                //两个相互的好友关系都要删除掉
                _db->erase_query<Relation>(query::user_id == user_id && query::peer_id == peer_id);
                _db->erase_query<Relation>(query::user_id == peer_id && query::peer_id == user_id);
                //提交事务
                trans.commit();
            }
            catch(const std::exception& e)
            {
                LOG_ERROR("删除好友关系 {} : {} 时失败, {}!", user_id, peer_id, e.what());
                return false;
            }
            return true; 
        }
        //判断两人是否为好友关系
        bool exists(const std::string& user_id, const std::string& peer_id)
        {
            //其实就是在好友关系表中查询是否存在好友关系数据
            //数据的所有操作都会有可能导致程序的崩溃，所以需要捕捉异常防止程序的退出；                
            typedef odb::query<Relation> query;
            typedef odb::result<Relation> result;
            bool flag = false;
            try
            {
                //3. 获取事务操作对象，开启事务
                odb::transaction trans(_db->begin());
                //4. 数据操作
                //_db->query<xxx>在构造的时候会自动的在查询条件前面加上 where ；
                result r = _db->query<Relation>(query::user_id == user_id && query::peer_id == peer_id); 
                flag = !r.empty(); 
                //5. 提交事务
                trans.commit();
            }
            catch(std::exception& e)
            {
                LOG_ERROR("判断好友关系 {} : {} 是否存在时失败, {}!", user_id, peer_id, e.what());
                return false;
            }

            return flag;
        }

        //获取指定用户的所有好友ID
        std::vector<std::string> friends(const std::string& user_id)
        {
            std::vector<std::string> res;
            //数据的所有操作都会有可能导致程序的崩溃，所以需要捕捉异常防止程序的退出；
            try
            {
                //3. 获取事务操作对象，开启事务
                odb::transaction trans(_db->begin());
                //4. 数据操作
                typedef odb::query<Relation> query;
                typedef odb::result<Relation> result;
                result r(_db->query<Relation>(query::user_id == user_id));
                for(auto it = r.begin(); it != r.end(); ++it)
                {
                    res.push_back(it->peer_id());
                }
                //5. 提交事务
                trans.commit();
            }
            catch(std::exception& e)
            {
                LOG_ERROR("获取用户 {} 的所有好友时失败, {}", user_id, e.what());
            }
            return res;
        }

    private:
        std::shared_ptr<odb::core::database> _db;
    };
}